package org.hyou.demos.msg.seri.adapter;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;

import org.hyou.demos.msg.seri.util.MessagePackUtil;
import org.msgpack.template.Template;
import org.msgpack.template.Templates;
import org.springframework.util.Assert;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;

/**
 * 类型匹配适配，针对getClassType方法进行的统一化的处理。
 * 结合缓存CacheUtil使用获取缓存数据时，再分别重写实现getJavaType方法为JSON反序列化指定构造类型
 * 
 * @param <T> 缓存数据对应的数据类型
 * @author Changshuo.Feng
 * @version 2.0.0 2015年1月9日 下午1:14:05 初始创建
 */
public abstract class AbstractTypeReferanceAdapter<T> implements MyTypeReference<T> {
    
    /**
     * 当前抽象类的泛型元素类型
     */
    private final Type type;
    
    /**
     * 在构造参数中获取泛型类型相关的信息
     * 
     * @throws IllegalArgumentException 不是泛型的声明形式会抛该异常
     */
    protected AbstractTypeReferanceAdapter() {
        Type superClass = super.getClass().getGenericSuperclass();
        if(superClass instanceof Class) {
            String errMsg = "Internal error: TypeReference constructed without actual type information";
            throw new IllegalArgumentException(errMsg);
        }
        
        this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }
    
    @SuppressWarnings("unchecked")
    @Override
    public Class<T> getClassType() {
        
        // 普通Class类型
        if(type instanceof Class) {
            return (Class<T>) type;
        }
        
        // 适用于泛型类型，例如List,Map之类
        return (Class<T>) ((ParameterizedType) type).getRawType();
    }
    
    @Override
    public JavaType getJavaType() {
        return getJavaType(type);
    }
    
    @Override
    public Template<T> getTemplate() {
        return getTemplate(type);
    }
    
    /**
     * <pre>
     * 根据类型Type构造出其相应的JavaType
     * 
     * 版本修改历史记录：
     * 1) 2.0.0 2015年1月15日 下午7:33:46 Changshuo.Feng 初始创建
     * </pre>
     * 
     * @param ptype 类型对象，有可能是简单的类型，也有可能是集合类型、并且集合中的元素参数也有可能嵌套着简单类型或集合类型
     * @return 构造后的JavaType对象
     * @throws UnsupportedOperationException 泛型类型中有不支持的类型处理时会抛该异常
     */
    private JavaType getJavaType(Type ptype) {
        
        TypeFactory typeFactory = TypeFactory.defaultInstance();
        
        // 普通类型
        if(ptype instanceof Class) {
            return typeFactory.constructType(ptype);
        }
        
        if(ptype instanceof ParameterizedType) {
            
            // 参数化类型
            ParameterizedType parameterizedType = (ParameterizedType) ptype;
            Type rawType = parameterizedType.getRawType();
            
            // 集合类List\Set等
            boolean isCollection = Collection.class.isAssignableFrom((Class<?>) rawType);
            if(isCollection) {
                return fromCollectionType(rawType, parameterizedType.getActualTypeArguments());
            }
            
            // Map类型
            boolean isMap = Map.class.isAssignableFrom((Class<?>) rawType);
            if(isMap) {
                return fromMapType(rawType, parameterizedType.getActualTypeArguments());
            }
            
        }
        
        throw new UnsupportedOperationException("不支持的类型处理");
    }
    
    /**
     * <pre>
     * 构造一个Collection集合类型的JavaType对象
     * 
     * 版本修改历史记录：
     * 1) 2.0.0 2015年1月15日 下午5:30:05 Changshuo.Feng 初始创建
     * </pre>
     * 
     * @param collectionRawType 集合类型，例如List、Set等
     * @param typeArgs 集合的参数类型，即泛型中的元素的类型
     * @return Collection集合类型的JavaType对象
     * @throws IllegalArgumentException typeArgs参数为空时抛该异常，typeArgs参数数组长度不等于1时会抛该异常
     */
    @SuppressWarnings("unchecked")
    private JavaType fromCollectionType(Type collectionRawType, Type[] typeArgs) {
        Assert.notEmpty(typeArgs, "Collection参数类型typeArgs不能为空");
        
        if(typeArgs.length != 1) {
            throw new IllegalArgumentException("Need exactly 1 parameter type for Collection types ("
                    + collectionRawType + ")");
        }
        
        JavaType elementJavaType = null;
        Type argType = typeArgs[0];
        
        TypeFactory typeFactory = TypeFactory.defaultInstance();
        
        if(argType instanceof Class) {
            
            // Collection泛型中放的是简单类型、或者简单的Bean类型
            elementJavaType = typeFactory.constructType(argType);
            
        } else if(argType instanceof ParameterizedType) {
            
            // Collection泛型中放的又是泛型类型
            elementJavaType = getJavaType(argType);
            
        }
        
        return typeFactory.constructCollectionType((Class<? extends Collection<?>>) collectionRawType, elementJavaType);
    }
    
    /**
     * <pre>
     * 构造一个Map类型的JavaType对象
     * 
     * 版本修改历史记录：
     * 1) 2.0.0 2015年1月15日 下午7:05:12 Changshuo.Feng 初始创建
     * </pre>
     *
     * @param mapRawType 集合类型，Map
     * @param typeArgs Map的参数类型数组，即泛型中的元素的类型，包括Key类型和Value类型两种
     * @return Map类型的JavaType对象
     * @throws IllegalArgumentException typeArgs参数为空时抛该异常，typeArgs参数数组长度不等于2时会抛该异常
     */
    private JavaType fromMapType(Type mapRawType, Type[] typeArgs) {
        Assert.notEmpty(typeArgs, "Map参数类型typeArgs不能为空");
        if(typeArgs.length != 2) {
            throw new IllegalArgumentException("Need exactly 2 parameter types for Map types (" + mapRawType + ")");
        }
        
        // 处理Key的JavaType
        Type key = typeArgs[0];
        JavaType keyType = getJavaType(key);
        
        // 处理Value的JavaType
        Type value = typeArgs[1];
        JavaType valueType = getJavaType(value);
        
        TypeFactory typeFactory = TypeFactory.defaultInstance();
        return typeFactory.constructMapType(Map.class, keyType, valueType);
    }
    
    /**
     * <pre>
     * 获取MessagePack的类型处理模板类对象，用于MessagePack数据处理
     * 
     * 版本修改历史记录：
     * 1) 2.0.0 2015年4月28日 下午5:09:47 Changshuo.Feng 初始创建
     * </pre>
     * 
     * @param ptype 类型对象，有可能是简单的类型，也有可能是集合类型、并且集合中的元素参数也有可能嵌套着简单类型或集合类型
     * @return MessagePack的类型处理模板类对象
     * @throws UnsupportedOperationException 泛型类型中有不支持的类型处理时会抛该异常
     */
    @SuppressWarnings("unchecked")
    private Template<T> getTemplate(Type ptype) {
        
        // 普通类型
        if(ptype instanceof Class) {
            return MessagePackUtil.lookup((Class<T>) ptype);
        }
        
        if(ptype instanceof ParameterizedType) {
            
            // 参数化类型
            ParameterizedType parameterizedType = (ParameterizedType) ptype;
            Type rawType = parameterizedType.getRawType();
            
            // 集合类List\Set等
            boolean isCollection = Collection.class.isAssignableFrom((Class<?>) rawType);
            if(isCollection) {
                return (Template<T>) getCollectionTemplate(rawType, parameterizedType.getActualTypeArguments());
            }
            
            // Map类型
            boolean isMap = Map.class.isAssignableFrom((Class<?>) rawType);
            if(isMap) {
                return (Template<T>) getMapTemplate(rawType, parameterizedType.getActualTypeArguments());
            }
            
        }
        
        throw new UnsupportedOperationException("不支持的类型处理");
        
    }
    
    /**
     * <pre>
     * 构造一个Collection集合类型的Template对象，用于MessagePack数据处理
     * 
     * 版本修改历史记录：
     * 1) 2.0.0 2015年4月29日 下午5:07:32 Changshuo.Feng 初始创建
     * </pre>
     * 
     * @param collectionRawType 集合类型，例如List、Set等
     * @param typeArgs 集合的参数类型，即泛型中的元素的类型
     * @return Collection集合类型的Template对象
     * @throws IllegalArgumentException typeArgs参数为空时抛该异常，typeArgs参数数组长度不等于1时会抛该异常
     */
    @SuppressWarnings("unchecked")
    private Template<?> getCollectionTemplate(Type collectionRawType, Type[] typeArgs) {
        Assert.notEmpty(typeArgs, "Collection参数类型typeArgs不能为空");
        
        if(typeArgs.length != 1) {
            throw new IllegalArgumentException("Need exactly 1 parameter type for Collection types ("
                    + collectionRawType + ")");
        }
        
        Type argType = typeArgs[0];
        
        if(argType instanceof Class) {
            
            // Collection泛型中放的是简单类型、或者简单的Bean类型
            Template<T> templateT = MessagePackUtil.lookupAndRegister((Class<T>) argType);
            return Templates.tList(templateT);
        } else if(argType instanceof ParameterizedType) {
            
            // Collection泛型中放的又是泛型类型
            return Templates.tList(getTemplate(argType));
        }
        
        throw new UnsupportedOperationException("不支持的类型处理");
    }
    
    /**
     * <pre>
     * 构造一个Map类型的Template对象，用于MessagePack数据处理
     * 
     * 版本修改历史记录：
     * 1) 2.0.0 2015年4月29日 下午5:08:53 Changshuo.Feng 初始创建
     * </pre>
     * 
     * @param mapRawType 集合类型，Map
     * @param typeArgs Map的参数类型数组，即泛型中的元素的类型，包括Key类型和Value类型两种
     * @return Map类型的Template对象
     * @throws IllegalArgumentException typeArgs参数为空时抛该异常，typeArgs参数数组长度不等于2时会抛该异常
     */
    private Template<?> getMapTemplate(Type mapRawType, Type[] typeArgs) {
        Assert.notEmpty(typeArgs, "Map参数类型typeArgs不能为空");
        if(typeArgs.length != 2) {
            throw new IllegalArgumentException("Need exactly 2 parameter types for Map types (" + mapRawType + ")");
        }
        
        // 处理Key的JavaType
        Type key = typeArgs[0];
        Template<?> keyTemplate = getTemplate(key);
        
        // 处理Value的JavaType
        Type value = typeArgs[1];
        Template<?> valueTemplate = getTemplate(value);
        
        return Templates.tMap(keyTemplate, valueTemplate);
    }
    
}
